package sourcelogin

import (
	"fmt"
	"freely/bin/comm"
	"freely/bin/crud"
	"freely/bin/utils"
	"freely/src/constant"
	"freely/src/methods"
	modelfront "freely/src/model/front"
	"image/color"
	"time"

	"github.com/mojocn/base64Captcha"
)

type SourceLoginer struct {
	*crud.Source
}

/**
 * 返回的token结构体
 * RefTime：刷新时间
 * ExpTime：过期时间
 */
type TokenSchema struct {
	Token   string `json:"token"`
	RefTime int64  `json:"refTime"`
	ExpTime int64  `json:"expTime"`
}

// 验证码结构体
type CaptchaParams struct {
	CaptchaID string `json:"captchaId"`
	Data      string `json:"data"`
}

/**
 * 用户登录逻辑。
 * 检查是否有该用户并返回对应token
 */
func (s *SourceLoginer) SourceLogin(username, password, loginAt string, exp time.Duration) (*TokenSchema, error) {
	var user = &modelfront.FrontUserStu{}
	var table = comm.Db.GetDB(nil)
	query := table.
		Where(
			"email=? OR phone=? OR username=? AND password=?",
			username,
			username,
			username,
			password,
		).
		Omit("password").First(user)
	if query.Error != nil {
		return nil, constant.ErrUserLogin
	}

	var role = &modelfront.FrontRoleStu{}
	query = table.Select("label").Where("id=?", user.ID).Find(role)
	if query.Error != nil {
		return nil, constant.ErrUserLogin
	}

	jwt := &methods.JwtData{
		UserID:   user.ID,
		Username: user.Username,
		RoleID:   user.RoleID,
		Role:     role.Label,
		LoginAt:  loginAt,
	}

	tokenSchema, err := getToken(jwt, exp)
	if err != nil {
		return nil, constant.ErrCreateToken
	}

	return tokenSchema, nil
}

/**
 * 用户注册
 */
func (s *SourceLoginer) SourceRegisterUser(user *modelfront.FrontUserStu) error {
	var table = comm.Db.GetDB(nil)

	// 默认是游客账号
	user.RoleID = 2
	user.DepartmentID = 3

	if user.Phone == "" {
		user.Phone = fmt.Sprintf("未绑定-%d", time.Now().Unix())
	}

	// 先检查，防止跳号
	query := table.Where("username=? OR phone=?", user.Username, user.Username).Select("username", "phone").Find(user)
	if query.RowsAffected > 0 {
		return constant.ErrUserExist
	}

	query = table.Create(user)
	return query.Error
}

/**
 * 刷新token
 */
func (s *SourceLoginer) RefreshToken(jwt *methods.JwtData, exp time.Duration) (*TokenSchema, error) {
	return getToken(jwt, exp)
}

/**
 * 创建验证码
 */
func (s *SourceLoginer) CaptchaCreate(w, h int) (*CaptchaParams, error) {
	config := base64Captcha.DriverString{
		Width:           w,
		Height:          h,
		Length:          4,
		Source:          "1234567890",
		ShowLineOptions: 2 | 4,
		NoiseCount:      0,
		BgColor:         &color.RGBA{255, 255, 255, 0}, // 只要数字的透明图
		Fonts:           []string{"wqy-microhei.ttc"},
	}

	drive := config.ConvertFonts()

	captcha := base64Captcha.NewCaptcha(drive, base64Captcha.DefaultMemStore)

	captchaId, base64Img, _, err := captcha.Generate()

	if err != nil {
		return nil, err
	}

	return &CaptchaParams{
		CaptchaID: captchaId,
		Data:      base64Img,
	}, nil
}

/**
 * 验证验证码
 */
func (s *SourceLoginer) CaptchaVerify(captchaId, captchaAns string) bool {
	if captchaId == "" || captchaAns == "" {
		return false
	}
	// cap := base64Captcha.DefaultMemStore.Get(captchaId, false)
	// fmt.Println("captchaCode", cap)
	return base64Captcha.DefaultMemStore.Verify(captchaId, captchaAns, true)
}

func getToken(jwt *methods.JwtData, expTime time.Duration) (*TokenSchema, error) {
	token, refTime, expires, err := methods.TokenCreate(jwt, expTime)
	if err != nil {
		return nil, err
	}

	// 前端使用的是毫秒数
	var tokenSchema = &TokenSchema{
		Token:   token,
		RefTime: refTime.Unix() * 1000,
		ExpTime: expires.Unix() * 1000,
	}

	// 存入redis，以便管理
	comm.RedisCli.Set(
		comm.AppCtx,
		utils.StrToHash(token),
		jwt.UserID,
		expTime,
	)

	// 在线用户
	comm.RedisCli.SAdd(comm.AppCtx, constant.RedisOnlineUsers, jwt.UserID)

	return tokenSchema, nil
}

/**
 * 返回登录操作数据源
 */
func NewSourceLoginer() *SourceLoginer {
	return &SourceLoginer{
		&crud.Source{},
	}
}
